package cn.yunbamboos.mybatis.register;

import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.TypeUtil;
import cn.yunbamboos.mybatis.annotation.CustomDelete;
import cn.yunbamboos.mybatis.annotation.CustomInsert;
import cn.yunbamboos.mybatis.annotation.CustomQuery;
import cn.yunbamboos.mybatis.annotation.CustomUpdate;
import cn.yunbamboos.mybatis.annotation.GenerateXML;
import cn.yunbamboos.mybatis.mapper.GenerateIdForIntMapper;
import cn.yunbamboos.mybatis.mapper.GenerateIdForLongMapper;
import cn.yunbamboos.mybatis.mapper.GenerateIdForStringMapper;
import cn.yunbamboos.mybatis.mapping.GenericMapping;
import cn.yunbamboos.mybatis.mapping.methods.generate.GenerateIdForIntMethod;
import cn.yunbamboos.mybatis.mapping.methods.generate.GenerateIdForLongMethod;
import cn.yunbamboos.mybatis.mapping.methods.generate.GenerateIdForStringMethod;
import cn.yunbamboos.mybatis.metadata.TableInfo;
import cn.yunbamboos.mybatis.metadata.TableInfoHelper;
import org.apache.ibatis.binding.MapperRegistry;
import org.apache.ibatis.session.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Collection;

/**
 * 通用的Mapper注册
 */
public class GenericMapperRegister {

    private static final Logger logger = LoggerFactory.getLogger(GenericMapperRegister.class);

    private final Configuration configuration;

    public GenericMapperRegister(Configuration configuration) {
        this.configuration = configuration;
    }

    /**
     * 读取mapper接口 {@link GenerateXML} 注解 生成对应的xml
     */
    public void register() {
        logger.info("start generate XML");
        MapperRegistry mapperRegistry = configuration.getMapperRegistry();
        Collection<Class<?>> mappers = mapperRegistry.getMappers();
        for (Class<?> mapper : mappers) {
            GenerateXML xml = mapper.getAnnotation(GenerateXML.class);
            if (ObjUtil.isNotNull(xml)) {
                this.register(mapper);
            }
        }
        logger.info("generate XML complete");
    }

    private void register(Class<?> mapper) {
        // 获取泛型
        Type[] types = TypeUtil.getTypeArguments(mapper);
        if (ObjUtil.isNotNull(types)) {
            for (Type type : types) {
                Class<?> clz = TypeUtil.getClass(type);
                TableInfo tableInfo = TableInfoHelper.initTableInfo(configuration, clz, mapper);
                GenericMapping handler = new GenericMapping(configuration, tableInfo);
                readCustomMethodFormMapper(mapper, handler);
                readGenerateMethodFormMapper(mapper, tableInfo, handler);
                handler.exec();
            }
        }
        logger.info("generate mapping {}", mapper);
    }

    /**
     * 获取接口上的自定义注解
     * {@link CustomInsert 自定义插入数据} </br>
     * {@link CustomDelete 自定义删除数据} </br>
     * {@link CustomUpdate 自定义更新数据} </br>
     * {@link CustomQuery 自定义查询数据} </br>
     */
    private void readCustomMethodFormMapper(Class<?> clz, GenericMapping handler) {
        Method[] methods = clz.getMethods();

        for (Method method : methods) {
            CustomInsert insert = method.getAnnotation(CustomInsert.class);
            if (ObjUtil.isNotNull(insert)) {
                handler.addCustomInsertMethod(method, insert);
            }
            CustomDelete delete = method.getAnnotation(CustomDelete.class);
            if (ObjUtil.isNotNull(delete)) {
                handler.addCustomDeleteMethod(method, delete);
            }
            CustomUpdate update = method.getAnnotation(CustomUpdate.class);
            if (ObjUtil.isNotNull(update)) {
                handler.addCustomUpdateMethod(method, update);
            }
            CustomQuery query = method.getAnnotation(CustomQuery.class);
            if (ObjUtil.isNotNull(query)) {
                handler.addCustomQueryMethod(method, query);
            }
        }
    }

    private void readGenerateMethodFormMapper(Class<?> clz, TableInfo tableInfo, GenericMapping handler) {
        if (GenerateIdForIntMapper.class.isAssignableFrom(clz)) {
            handler.addGenerateMethod(new GenerateIdForIntMethod(configuration, tableInfo));
        }
        if (GenerateIdForLongMapper.class.isAssignableFrom(clz)) {
            handler.addGenerateMethod(new GenerateIdForLongMethod(configuration, tableInfo));
        }
        if (GenerateIdForStringMapper.class.isAssignableFrom(clz)) {
            handler.addGenerateMethod(new GenerateIdForStringMethod(configuration, tableInfo));
        }
    }


}
